package il.ac.technion.cs236700.address_book;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import il.ac.technion.cs236700.address_book.Address;
import il.ac.technion.cs236700.address_book.AddressBook;
import il.ac.technion.cs236700.address_book.AddressBookImpl;
import il.ac.technion.cs236700.address_book.Contact;
import il.ac.technion.cs236700.address_book.Defaults;
import il.ac.technion.cs236700.address_book.FlexibleAddressBookImpl;
import il.ac.technion.cs236700.address_book.Person;
import il.ac.technion.cs236700.address_book.Samples;
import il.ac.technion.cs236700.utils.Misc;
import il.ac.technion.cs236700.utils.STRING;
import il.ac.technion.cs236700.utils.CSV.Separator;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class FlexibleAddressBook_Test extends AddressBookImpl_Test{

public static ArrayList<Contact> CONTACTS = new ArrayList<Contact>();	
	
	protected FlexibleAddressBookImpl newFlexibleAddressBook(){
		AddressBook ab = super.newAddressBook();
		FlexibleAddressBookImpl gab = 
			new FlexibleAddressBookImpl(((AddressBookImpl)ab).getContactsCopy());		
		return gab;
	}
	
	@Before
	public void setUp() throws Exception {
		CONTACTS.add(new Contact(Misc.map(Defaults.FirstName, "Ran").map(Defaults.LastName,"Away")
		         .map(Defaults.HomePage,"http://www.imdb.com/title/tt0910970").map));
		CONTACTS.add(new Contact(Misc.map(Defaults.FirstName, "Dan").map(Defaults.LastName,"Brown")
		         .map(Defaults.HomePhone,"04-1234567").map(Defaults.Company,"RCA inc.").map));
		CONTACTS.add(new Contact(Misc.map(Defaults.FirstName, "Benny").map(Defaults.LastName,"Lev")
		         .map(Defaults.HomePage,"http://www.google.com").map));
		CONTACTS.add(new Contact(Misc.map(Defaults.FirstName, "Simon").map(Defaults.LastName,"Jensen")
		         .map(Defaults.Title,"Sir").map));
		CONTACTS.add(new Contact(Misc.map(Defaults.FirstName, "Ann").map(Defaults.LastName,"Devola")
		         .map(Defaults.Email,"annd@gmail.com").map));
		CONTACTS.add(new Contact(Misc.map(Defaults.FirstName, "Peter").map(Defaults.LastName,"Cargil")
		         .map(Defaults.HomePhone,"972-4-5556669").map));
		CONTACTS.add(new Contact(Misc.map(Defaults.FirstName, "Pam").map(Defaults.LastName,"Daugh")
		         .map(Defaults.Company,"AT&T").map));
		CONTACTS.add(new Contact(Misc.map(Defaults.FirstName, "Fionna").map(Defaults.LastName,"May")
		         .map(Defaults.HomePage,"www.amazon.com").map));
		CONTACTS.add(new Contact(Misc.map(Defaults.FirstName, "Kenny").map(Defaults.LastName,"Benya")
		         .map(Defaults.Email,"kbsu@seinfeld.com").map));
		CONTACTS.add(new Contact(Misc.map(Defaults.FirstName, "Adam").map(Defaults.LastName,"Adam")
		         .map(Defaults.HomePage,"adam.homepage.com").map));
	
	}

	@After
	public void tearDown() throws Exception {
	}

	@Test
	public void testAddPersonAddressAddress() {
		FlexibleAddressBookImpl ab = newFlexibleAddressBook(); 
		Person p = new Person("Rick", "Deckard", "");
	    Address add = new Address("", "LAPD", "LA", "", "");  
		ab.add(p,null,add);
	    assertEquals(true, ab.exists(p));	
	}


	@Test
	public void testGetConflicts() {
		FlexibleAddressBookImpl ab = newFlexibleAddressBook();
		ab.add(Misc.map(Defaults.FirstName, "David")
				.map(Defaults.LastName, "Gal").map(Defaults.Email,"dave@walla").map);
		ab.add(Misc.map(Defaults.FirstName, "David")
				.map(Defaults.LastName, "Gal").map(Defaults.Email,"dave@nana").map);
		ab.setOutput(Defaults.FirstName);
		String $ = Separator.separateBy(ab.getOutputFields(), ",")+"\n";
		$ += "David"+"\n" ;
		$ += "David"+"\n";
		assertEquals($,ab.getConflicts());

	}

	@Test
	public void testGetNormalized() {
		FlexibleAddressBookImpl ab = new FlexibleAddressBookImpl(new ArrayList<Contact>());
		ab.setOutput(Samples.FIELDS_1);
		ab.load(Samples.CSV1);
		Assert.assertEquals(Samples.CSV2, ab.getNormalized());
	}

	@Test
	public void testGetSupportedKeys() {
		FlexibleAddressBookImpl ab = newFlexibleAddressBook();
		String $1 = STRING.cat(Defaults.SUPPORTED_KEYS.toArray(new String[Defaults.SUPPORTED_KEYS.size()]));
		String $2 = STRING.cat(ab.getSupportedKeys());
		Assert.assertEquals($1,$2);
	}

	@Test
	public void testLoad() {
		FlexibleAddressBookImpl ab = new FlexibleAddressBookImpl(new ArrayList<Contact>());
	      ab.setOutput(Samples.FIELDS_1);
	      ab.load(Samples.CSV2);
	      Assert.assertEquals(Samples.CSV2, ab.getNormalized());
	}

	@Test
	public void testSetOutput() {
		FlexibleAddressBookImpl ab = newFlexibleAddressBook();
		ab.setOutput(Samples.FIELDS_2);
		assertEquals(STRING.cat(Samples.FIELDS_2), STRING.cat(ab.getOutputFields())); 
		
	}

	@Test
	public void testFlexibleAdressBookImpl() {
		FlexibleAddressBookImpl fab = new FlexibleAddressBookImpl(CONTACTS);
		assertArrayEquals(
				CONTACTS.toArray(), fab.getContacts().toArray());
	}

	@Test
	public void testAddGroup() {
		FlexibleAddressBookImpl fab = new FlexibleAddressBookImpl(CONTACTS);
		fab.AddGroup("g1");
		fab.AddGroup("g1/g2");
		fab.AddGroup("g1/g2/g3");
		fab.AddGroup("g2");
		fab.AddGroup("g2/g2");
		assertArrayEquals(
				new ArrayList<Contact>().toArray(), fab.getGroupContacts("g1").toArray());
	}
	/*
	@Test(expected=InvalidPathException.class)
	public void testAddGroupFail(){
		FlexibleAddressBookImpl fab = new FlexibleAddressBookImpl(CONTACTS);
		fab.AddGroup("g3/g2");	
	}*/
	
	@Test
	public void testAddToGroup() {
		FlexibleAddressBookImpl fab = new FlexibleAddressBookImpl(CONTACTS);
		fab.AddGroup("g1");
		ArrayList<Contact> expected = new ArrayList<Contact>();
		Contact c = new Contact();
		c.setFirstName("Ben");c.setLastName("Gordon");c.setNewEntry("Title", "");
		expected.add(c);
		fab.AddToGroup("g1", new Person("Ben","Gordon",""));
		assertArrayEquals(expected.toArray(), fab.getGroupContacts("g1").toArray());
		c = new Contact();
		c.setFirstName("Adam");c.setLastName("Adam");
		c.setNewEntry(Defaults.HomePage, "adam.homepage.com");
		expected.add(c);
		Collections.sort(expected);
		fab.AddToGroup("g1", new Person("Adam","Adam",""));
		assertArrayEquals(expected.toArray(), fab.getGroupContacts("g1").toArray());

	}

	@Test
	public void testDeleteGroup() {
		FlexibleAddressBookImpl fab = newFlexibleAddressBook();
		fab.AddGroup("g1");
		fab.AddGroup("g1/g2");
		fab.AddGroup("g1/g2/g3");
		fab.AddGroup("g2");
		fab.AddGroup("g2/g2");
		fab.DeleteGroup("g2");
		assertEquals(null, fab.getGroupContacts("g2"));
		fab.DeleteGroup("g1/g2");
		assertArrayEquals(
				new ArrayList<Contact>().toArray(), fab.getGroupContacts("g1").toArray());
		assertEquals(null, fab.getGroupContacts("g1/g2/g3"));
	}

	@Test
	public void testRemoveFromGroup() {
		FlexibleAddressBookImpl fab = newFlexibleAddressBook();
		fab.AddGroup("g1");
		ArrayList<Contact> expected = new ArrayList<Contact>();
		Contact c = new Contact();
		c.setFirstName("Avi");c.setLastName("Gidon");c.setNewEntry("Title", "Mr");
		expected.add(c);
		Person p = new Person("Avi","Gidon","Mr");
		fab.AddToGroup("g1",p);
		assertArrayEquals(
				expected.toArray(), fab.getGroupContacts("g1").toArray());
		fab.RemoveFromGroup("g1", p);
		assertEquals(new ArrayList<Contact>(), fab.getGroupContacts("g1"));
	}

	@Test
	public void testGetContainedGroups() {
		FlexibleAddressBookImpl fab = newFlexibleAddressBook();
		Person p = new Person("Dan","Brown","");
		fab.AddGroup("g1");
		fab.AddGroup("g1/g2");
		fab.AddGroup("g1/g2/g3");
		fab.AddGroup("g2");
		fab.AddGroup("g2/g2");
		fab.AddToGroup("g1",p);
		fab.AddToGroup("g2",p);
		fab.AddToGroup("g1/g2",p);
		String[] expected = {"g1","g1/g2","g2"};
		assertArrayEquals(expected, fab.getContainedGroups(p).toArray());
	}

	@Test
	public void testGetGroupContacts() {
		FlexibleAddressBookImpl fab = newFlexibleAddressBook();
		Person p = new Person("Dan","Brown","");
		fab.AddGroup("g1");
		fab.AddGroup("g1/g2");
		fab.AddGroup("g1/g2/g3");
		fab.AddToGroup("g1",p);
		fab.AddToGroup("g1/g2",p);
		fab.AddToGroup("g1/g2/g3",p);
		Contact c = new Contact();
		c.setFirstName("Dan");c.setLastName("Brown");c.setNewEntry(Defaults.Title, "");
		Contact[] expected = {c};
		assertArrayEquals(expected, fab.getGroupContacts("g1").toArray());
	}
	@Test
	public void testAddDetails(){
		Map<String, String> map = new HashMap<String, String>();
		map.put("FirstName","Doner" );
		map.put("LastName","Bural" );
		map.put("Title","" );
		map.put("Mobile Phone","0467578" );
		Contact expected = new Contact(map);
		ArrayList<Contact> conacts = new ArrayList<Contact>();
		FlexibleAddressBookImpl fab = new FlexibleAddressBookImpl(conacts);
		Person p = new Person("Doner", "Bural", "");
		fab.add(p, null, null);
		Map<String, String> details = new HashMap<String, String>();
		details.put("Mobile Phone", "0467578");
		fab.AddDetails(p, details);
		Assert.assertEquals(fab.find(p).getMap().size(), expected.getMap().size());
	}

	@Test
	public void testAddSupportedKeys() {
		FlexibleAddressBookImpl fab = newFlexibleAddressBook();
		int oldLength = fab.getSupportedKeys().length;
		ArrayList<String> keys = new ArrayList<String>();
		keys.add("Nickname");
		fab.AddSupportedKeys(keys);
		int newLength =  fab.getSupportedKeys().length;
		assertEquals(oldLength+1, newLength);
	}

	@Test
	public void testDelete(){
		Person p = new Person("Doner", "Bural", "");
		ArrayList<Contact> conacts = new ArrayList<Contact>();
		Contact c = new Contact();
		c.setFirstName("Doner");c.setLastName("Bural");c.setNewEntry("Title", "");
		conacts.add(c);
		FlexibleAddressBookImpl cab = new FlexibleAddressBookImpl(conacts);
		cab.Delete(p);
		Assert.assertEquals(cab.getContacts().size(), 0);
	}

	@Test
	public void testDeleteDetails(){
		Map<String, String> map = new HashMap<String, String>();
		map.put("FirstName","Doner" );
		map.put("LastName","Bural" );
		map.put("Title","" );
		Contact expected = new Contact(map);
		ArrayList<Contact> conacts = new ArrayList<Contact>();
		FlexibleAddressBookImpl cab = new FlexibleAddressBookImpl(conacts);
		Person p = new Person("Doner", "Bural", "");
		cab.add(p, null, null);
		Map<String, String> details = new HashMap<String, String>();
		details.put("Mobile Phone", "0467578");
		String[] keys = (String[]) details.keySet().toArray(new String[details.keySet().size()]);
		cab.AddDetails(p, details);
		cab.DeleteDetails(p, keys);
		Assert.assertEquals(cab.find(p).getMap().size(), expected.getMap().size());
	}

	@Test
	public void testUpdateDetails() {
		ArrayList<Contact> conacts = new ArrayList<Contact>();
		FlexibleAddressBookImpl fab = new FlexibleAddressBookImpl(conacts);
		Person p = new Person("Orna", "Banai", "");
		fab.add(p, null, null);
		Map<String, String> newDetails = new HashMap<String, String>();
		newDetails.put("First Name", "Meir");
		fab.UpdateDetails(p, newDetails);
		Assert.assertEquals("Meir", fab.find(new Person("Meir", "Banai", "")).FirstName());
	}

	@Test
	public void testDefineAgreement() {
	}

	@Test
	public void testDefineConflict() {
	}

	@Test
	public void testDefineUnify() {
	}

	@Test
	public void testSearch(){
		String expected = "First Name,Last Name,Home Phone,Mobile Phone,"
			+"Work Phone,Fax,Email,Instant Messanger Type,Instant Messanger ID,"+
			"Home Address,Work Address,Home page,Title,Company\n"+
			"Rehan,Adam,,,,,,,,,,,Mr,\n"+			
			"Adan,Rega,,,,,,,,,,,,\n";
		ArrayList<Contact> conacts = new ArrayList<Contact>();
		FlexibleAddressBookImpl fab = new FlexibleAddressBookImpl(conacts);
		Person p = new Person("Doner", "Bural", "");
		Person p1 = new Person("Rehan", "Adam", "Mr");
		Person p2 = new Person("Adan", "Rega","");
		fab.add(p1, null, null);
		fab.add(p,null, null);
		fab.add(p2,null, null);
		Assert.assertEquals(fab.Search("Re"), expected);
	}

	@Test
	public void testSearchAccordingToKey(){
		LinkedList<String> expected = new LinkedList<String>();
		expected.addLast("Adam");
		expected.addLast("Doner");
		ArrayList<Contact> conacts = new ArrayList<Contact>();
		FlexibleAddressBookImpl fab = new FlexibleAddressBookImpl(conacts);
		Person p = new Person("Doner", "Bural", "");
		Person p1 = new Person("Adam", "Oguz", "Mr");
		Address add = new Address("", "LAPD", "LA", "", "");  
		fab.add(p, null, null);
		fab.add(p1,null, add);
		ArrayList<String> $ = fab.SearchAccordingToKey("First Name");
		Assert.assertEquals(expected.size(), $.size());
		Assert.assertEquals(expected.get(0), $.get(0));
		Assert.assertEquals(expected.get(1), $.get(1));
	}

	@Test
	public void testSearchForPerson(){
		String expected = "Last Name,Title,First Name\nBural,,Doner\n";
		ArrayList<Contact> conacts = new ArrayList<Contact>();
		FlexibleAddressBookImpl fab = new FlexibleAddressBookImpl(conacts);
		Person p = new Person("Doner", "Bural", "");
		fab.add(p, null, null);
		String $ = fab.SearchForPerson(p);
		Assert.assertEquals(expected,$);
	}

	@Test
	public void testSearchByMap(){
		String expected = "First Name,Last Name,Home Phone,Mobile Phone,"
			+"Work Phone,Fax,Email,Instant Messanger Type,Instant Messanger ID,"+
			"Home Address,Work Address,Home page,Title,Company\n"+
			"Adam,Bural,,,,,,,,,LAPD LA,,Mr,\n"+			
			"Doner,Bural,,,,,,,,,,,,\n";

		Map<String, String> map = new HashMap<String, String>();
		map.put("Last Name", "Bural");
		ArrayList<Contact> conacts = new ArrayList<Contact>();
		FlexibleAddressBookImpl fab = new FlexibleAddressBookImpl(conacts);
		Person p = new Person("Doner", "Bural", "");
		Person p1 = new Person("Adam", "Bural", "Mr");
		Address add = new Address("", "LAPD", "LA", "", "");  
		fab.add(p, null, null);
		fab.add(p1,null, add);
		Assert.assertEquals(fab.Search(map), expected);
	}

}
